package cn.shujuhai.cms.service.impl;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: zhangDeZhong
 * @Date: 2024/12/5 12:01
 * @Description: 针对表静态模板文件elfinder的Service实现
 **/
@Service
public class ElfinderServiceImpl {

    // 配置你的文件根目录
    @Value("${template.static.fileUrl}")
    private String rootDirectory;

    @Value("${server.servlet.context-path}")
    private String baseUrl;

    //存储文件与哈希的map集合
    private final Map<String, String> hashToPathMap = new HashMap<>();

    /**
     * 初始化map集合
     *
     * @throws IOException IOException
     */
    public void initializeHashMapping() throws IOException {
        Path rootPath = Paths.get(rootDirectory);

        Files.walk(rootPath)
                .forEach(path -> {
                    String hash = String.valueOf(path.toString().hashCode());
                    hashToPathMap.put(hash, path.toString());
                });
    }

    /**
     * 根据哈希值获取实际路径
     *
     * @param targetHash targetHash
     * @return String
     */
    private String getPathFromTarget(String targetHash) {
        return hashToPathMap.get(targetHash);
    }

    /**
     * 处理来自 elFinder 的请求
     *
     * @param params  params
     * @param targets targets
     * @return Map<String, Object>
     * @throws IOException IOException
     */
    public Map<String, Object> handleRequest(Map<String, String> params, List<String> targets) throws IOException {
        initializeHashMapping();
        Map<String, Object> response = new HashMap<>();
        String cmd = params.get("cmd");
        String target = params.get("target");  // 获取前端传递的 target 参数
        // 通过 target 哈希值来解析文件夹的实际路径
        String path = getPathFromTarget(target);
        try {
            if ("open".equals(cmd)) {
                response = openDirectory(path);
            } else if ("rm".equals(cmd)) {
                response = removeFileOrDirectory(params, targets);
            } else if ("mkfile".equals(cmd)) {
                String fileName = params.get("name");
                response = createFile(path, fileName);
            } else if ("mkdir".equals(cmd)) {
                String folderName = params.get("name");
                response = createDirectory(path, folderName);
            } else if ("rename".equals(cmd)) {
                String rename = params.get("name");
                response = renameFileOrDirectory(path, rename);
            }
            // 其他命令可以继续处理
            response.put("status", 0); // 0 表示成功

        } catch (IOException e) {
            e.printStackTrace();
            response.put("status", 1); // 错误
            response.put("error", "操作失败: " + e.getMessage());
        }
        return response;
    }

    /**
     * 打开目录的逻辑
     *
     * @param path path
     * @return Map<String, Object>
     * @throws IOException IOException
     */
    private Map<String, Object> openDirectory(String path) throws IOException {
        Map<String, Object> response = new HashMap<>();

        // 如果没有传递路径，默认打开根目录
        Path cwdPath = Path.of((path == null || path.isEmpty()) ? rootDirectory : path);

        if (Files.isRegularFile(cwdPath)) {
            String fileContent = new String(Files.readAllBytes(cwdPath), StandardCharsets.UTF_8);
            response.put("status", 0);  // 表示成功
            response.put("fileContent", fileContent);  // 添加文件内容到响应
            return response;
        }

        if (!Files.exists(cwdPath) || !Files.isDirectory(cwdPath)) {
            response.put("status", 1); // 错误
            response.put("error", "指定的目录不存在");
            return response;
        }

        // 创建 cwd 对象
        Map<String, Object> cwd = createCwdObject(cwdPath);
        response.put("cwd", cwd);

        // 创建 cdc 列表（目录和文件项列表）
        List<Map<String, Object>> cdc = createCdcList(cwdPath);
        response.put("cdc", cdc);


        // 创建 tree 结构
        Map<String, Object> tree = createTreeStructure(Paths.get(rootDirectory));
        response.put("tree", tree);

        // 返回其他 elFinder 必需的参数
        response.put("disabled", new ArrayList<>()); // 禁用的功能
        response.put("params", Map.of(
                "archives", List.of(),
                "extract", List.of(),
                "dotFiles", true,
                "uplMaxSize", "50M",
                "url", "/elfinder/download?p="
        ));

        return response;
    }


    /**
     * 创建 cwd 对象
     *
     * @param cwdPath cwdPath
     * @return Map<String, Object>
     * @throws IOException IOException
     */
    private Map<String, Object> createCwdObject(Path cwdPath) throws IOException {
        Map<String, Object> cwd = new HashMap<>();
        cwd.put("date", Files.getLastModifiedTime(cwdPath).toString());
        cwd.put("read", Files.isReadable(cwdPath));
        cwd.put("size", "0");
        cwd.put("mime", "directory");
        cwd.put("name", cwdPath.getFileName().toString());
        cwd.put("rel", cwdPath.getFileName().toString());
        cwd.put("rm", Files.isWritable(cwdPath));
        cwd.put("write", Files.isWritable(cwdPath));
        cwd.put("hash", cwdPath.toString().hashCode() + "");
        return cwd;
    }

    /**
     * 创建 cdc 列表（目录和文件项列表）
     *
     * @param cwdPath cwdPath
     * @return List<Map < String, Object>>
     * @throws IOException IOException
     */
    private List<Map<String, Object>> createCdcList(Path cwdPath) throws IOException {
        List<Map<String, Object>> cdc = new ArrayList<>();
        Files.list(cwdPath).forEach(path -> {
            Map<String, Object> item = new HashMap<>();
            try {
                item.put("date", Files.getLastModifiedTime(path).toString());
                item.put("read", Files.isReadable(path));
                item.put("size", Files.isDirectory(path) ? 0 : Files.size(path));
                item.put("mime", Files.isDirectory(path) ? "directory" : Files.probeContentType(path));
                item.put("name", path.getFileName().toString());
                item.put("rm", Files.isWritable(path));
                item.put("write", Files.isWritable(path));
                item.put("hash", String.valueOf(path.toString().hashCode()));

                // 处理文件的缩略图
                if (Files.isRegularFile(path)) {
                    // 如果是图片文件，生成缩略图
                    if (path.toString().endsWith(".png") || path.toString().endsWith(".jpg") || path.toString().endsWith(".jpeg") || path.toString().endsWith(".gif")) {
                        // 将路径中的反斜杠替换为正斜杠
                        String formattedPath = path.toString().replace("\\", "/");

                        // 生成缩略图的 URL
                        String tmbUrl = baseUrl + "elfinder/thumbnail?p=" + formattedPath; // 缩略图路径
                        item.put("tmb", tmbUrl); // 添加缩略图
                    } else {
                        item.put("tmb", ""); // 非图片文件不返回缩略图
                    }

                    // 文件的访问 URL
                    String fileUrl = baseUrl + "elfinder/virtualproxy?p=" + path.toString().replace("\\", "/");
                    item.put("url", fileUrl); // 添加文件的访问 URL
                }


                cdc.add(item);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        return cdc;
    }


    /**
     * 创建 tree 结构（递归遍历目录树）
     *
     * @param directory directory
     * @return Map<String, Object>
     * @throws IOException IOException
     */
    private Map<String, Object> createTreeStructure(Path directory) throws IOException {
        Map<String, Object> tree = new HashMap<>();
        tree.put("read", Files.isReadable(directory));
        tree.put("name", directory.getFileName().toString());
        tree.put("write", Files.isWritable(directory));
        tree.put("hash", directory.toString().hashCode() + "");

        List<Map<String, Object>> dirs = new ArrayList<>();
        Files.list(directory)
                .filter(Files::isDirectory)
                .forEach(subDir -> {
                    try {
                        dirs.add(createTreeStructure(subDir));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
        tree.put("dirs", dirs);

        return tree;
    }

    /**
     * 创建文件
     *
     * @param parentPath parentPath
     * @param fileName   fileName
     * @return Map<String, Object>
     * @throws IOException IOException
     */
    private Map<String, Object> createFile(String parentPath, String fileName) throws IOException {
        Map<String, Object> response = new HashMap<>();
        Path filePath = Paths.get(parentPath, fileName);

        if (Files.exists(filePath)) {
            response.put("status", 1);
            response.put("error", "文件已存在");
        } else {
            try {
                Files.createFile(filePath);  // 创建文件
                initializeHashMapping();
                response = openDirectory(parentPath);
                response.put("status", 0);
                response.put("name", fileName);
                response.put("path", filePath.toString());
            } catch (IOException e) {
                response.put("status", 1);  // 错误
                response.put("error", "无法创建文件: " + e.getMessage());
            }
        }

        return response;
    }


    /**
     * 创建文件夹
     *
     * @param parentPath parentPath
     * @param folderName folderName
     * @return Map<String, Object>
     * @throws IOException IOException
     */
    private Map<String, Object> createDirectory(String parentPath, String folderName) throws IOException {
        Map<String, Object> response = new HashMap<>();
        Path directoryPath = Paths.get(parentPath, folderName);

        if (Files.exists(directoryPath)) {
            response.put("status", 1);
            response.put("error", "文件夹已存在");
        } else {
            try {
                Files.createDirectory(directoryPath);
                initializeHashMapping();
                response = openDirectory(parentPath);
                response.put("status", 0);
                response.put("name", folderName);
                response.put("path", directoryPath.toString());
            } catch (IOException e) {
                response.put("status", 1);  // 错误
                response.put("error", "无法创建文件夹: " + e.getMessage());
            }
        }

        return response;
    }

    /**
     * 重命名文件或文件夹
     *
     * @param path   path
     * @param rename rename
     * @return Map<String, Object>
     * @throws IOException IOException
     */
    private Map<String, Object> renameFileOrDirectory(String path, String rename) throws IOException {
        Map<String, Object> response = new HashMap<>();
        Path sourcePath = Paths.get(path);
        Path targetPath = Paths.get(sourcePath.getParent().toString(), rename);

        if (!Files.exists(sourcePath)) {
            response.put("status", 1);
            response.put("error", "原文件或文件夹不存在");
        } else if (Files.exists(targetPath)) {
            response.put("status", 1);
            response.put("error", "目标文件或文件夹已存在");
        } else {
            try {
                Files.move(sourcePath, targetPath);  // 重命名操作
                initializeHashMapping();
                response = openDirectory(sourcePath.getParent().toString());
                response.put("status", 0);
                response.put("name", rename);
                response.put("path", targetPath.toString());
            } catch (IOException e) {
                response.put("status", 1);
                response.put("error", "无法重命名: " + e.getMessage());
            }
        }
        return response;
    }


    /**
     * 处理上传文件
     *
     * @param params params
     * @param files  files
     * @return Map<String, Object>
     * @throws IOException IOException
     */
    public Map<String, Object> uploadFile(Map<String, String> params, MultipartFile[] files) throws IOException {
        initializeHashMapping();
        String current = params.get("current");
        String nowMenu = getPathFromTarget(current);

        Map<String, Object> response = new HashMap<>();

        // 文件上传处理
        if (files != null && files.length > 0) {
            List<Map<String, Object>> addedFiles = new ArrayList<>();
            for (MultipartFile file : files) {
                if (!file.isEmpty()) {
                    try {
                        // 保存文件
                        String fileString = nowMenu + "/" + file.getOriginalFilename();
                        Path filePath = Path.of(fileString);
                        file.transferTo(filePath);

                        // 生成文件信息
                        Map<String, Object> fileInfo = new HashMap<>();
                        fileInfo.put("name", filePath.getFileName());
                        fileInfo.put("hash", Integer.toString(filePath.hashCode())); // 生成文件的唯一hash
                        fileInfo.put("mime", Files.isDirectory(filePath) ? "directory" : Files.probeContentType(filePath));
                        fileInfo.put("size", Files.isDirectory(filePath) ? 0 : Files.size(filePath));
                        fileInfo.put("read", true);
                        fileInfo.put("write", true);
                        fileInfo.put("rm", true);
                        if (filePath.toString().endsWith(".png") || filePath.toString().endsWith(".jpg") || filePath.toString().endsWith(".jpeg") || filePath.toString().endsWith(".gif")) {
                            // 将路径中的反斜杠替换为正斜杠
                            String formattedPath = filePath.toString().replace("\\", "/");

                            // 生成缩略图的 URL
                            String tmbUrl = baseUrl + "elfinder/thumbnail?p=" + formattedPath; // 缩略图路径
                            fileInfo.put("tmb", tmbUrl); // 添加缩略图
                        } else {
                            fileInfo.put("tmb", ""); // 非图片文件不返回缩略图
                        }

                        // 文件的访问 URL
                        String fileUrl = baseUrl + "elfinder/virtualproxy?p=" + filePath.toString().replace("\\", "/");
                        fileInfo.put("url", fileUrl); // 添加文件的访问 URL

                        addedFiles.add(fileInfo);

                    } catch (IOException e) {
                        response.put("error", "文件上传失败: " + e.getMessage());
                        return response;
                    }
                } else {
                    response.put("error", "未上传文件");
                    return response;
                }
            }
            initializeHashMapping();
            response = openDirectory(nowMenu);
        } else {
            response.put("error", "未上传文件");
        }

        return response;
    }


    /**
     * 删除文件或目录
     *
     * @param params  params
     * @param targets targets
     * @return Map<String, Object>
     * @throws IOException IOException
     */
    public Map<String, Object> removeFileOrDirectory(Map<String, String> params, List<String> targets) throws IOException {
        initializeHashMapping();
        //根据params里面的current获取hash值，根据 getPathFromTarget()获取到路径，然后for循环targets里面的值，getPathFromTarget()获取到路径，在删除文件
        String current = params.get("current");
        String nowMenu = getPathFromTarget(current);
        Map<String, Object> response = new HashMap<>();

        for (String target : targets) {
            // 通过 target 获取到文件的路径
            String filePath = getPathFromTarget(target);
            if (filePath != null) {
                File fileOrDir = new File(filePath);
                if (fileOrDir.exists()) {

                    boolean deleted = deleteFileOrDirectory(fileOrDir);  // 执行删除操作
                    initializeHashMapping();
                    if (fileOrDir.isDirectory()) {
                        nowMenu = fileOrDir.getParent();
                    }
                    response = openDirectory(nowMenu);
                    if (deleted) {
                        response.put("status", 0); // 删除成功
                        response.put("msg", "删除成功");
                    } else {
                        response.put("status", 1); // 删除失败
                        response.put("error", "删除失败");
                    }
                } else {
                    response.put("status", 1);
                    response.put("error", "文件或目录不存在");
                }
            } else {
                response.put(target, "无法找到文件路径");
            }
        }

        return response;
    }

    /**
     * 递归删除文件或目录下的文件
     *
     * @param fileOrDir fileOrDir
     * @return boolean
     */
    private boolean deleteFileOrDirectory(File fileOrDir) {
        if (fileOrDir.isDirectory()) {
            // 递归删除目录内容
            String[] files = fileOrDir.list();
            if (files != null) {
                for (String file : files) {
                    File child = new File(fileOrDir, file);
                    deleteFileOrDirectory(child);  // 递归删除子文件或子目录
                }
            }
        }
        return fileOrDir.delete();  // 删除文件或目录
    }
}
